// 单波段数据计数/suming合成，符合特定像素值的计数加1，反正其他情况保留0即可。
// this is for scan dirs . 
// 2024/9/27

#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include "wstringutils.h"
#include "wGdalRaster.h"
#define WGDALRASTER_H_IMPLEMENTATION
#include "wGdalRaster.h"
#include <iostream>
#include "gdal_priv.h"
#include "ogr_spatialref.h"
#include <memory>
#include "wdirtools.h"
#include "getopt_pp.h"
#include "wstringutils.h"

using namespace std;
using namespace GetOpt;

string GTYPE[]={"none", "byte","u16","i16","u32","i32","f32","f64"} ;



template<typename T>
bool FillOneFileIntoOutput( 
	vector<T>& sumdata ,
	vector<int>& cntdata , //same size with sumDataPtr
	const int outxsize,
	const int outysize,
	string oneInputFile ,
	double vmin ,
	double vmax , 
	double fillvalue ,
	string& error
	)
{
	cout<<"begin for "<<oneInputFile<<endl ;
	shared_ptr<wGdalRaster> inRas(wGdalRasterFactory::OpenFile( oneInputFile )) ;

	if( inRas.get() == 0 ){
		error = oneInputFile + " is bad";
		return false ;
	}
	
	const int inxsize = inRas->getXSize() ;
	const int inysize = inRas->getYSize() ;

	if( inxsize!= outxsize || inysize != outysize )
	{
		error = oneInputFile + " have different size." ;
		return false ;
	} 
	int per0 =  -1;
	for(int oy = 0 ; oy < outysize ; ++ oy )
	{
		for(int ox =0 ; ox < outxsize ; ++ ox )
		{
			double inval = inRas->getValued(ox,oy,0) ;
			if( inval >= vmin && inval <= vmax )
			{
				int it = oy * outxsize + ox ;
				sumdata[it] += inval ;
				cntdata[it]+=1 ;
			}	
		}
	}
	cout<<" ok "<<endl ;
	return true ;
}

template<typename T>
bool doAllFiles( vector<string>& infileVec ,
	int xsize,int ysize,double valid0,double valid1,double fillvalue,
	GDALDataType outType,
	double* trans,
	char* proj,
	string outFilename, bool skipbad, string type )
{
	int asize = xsize*ysize ;
	vector<T> sumdata(asize) ;
	vector<int> cntdata(asize,0) ;
	for(int it = 0;it<asize;++it) sumdata[it] = 0 ;
	cout<<"suming and counting ..."<<endl ;
	for(int iin = 0 ; iin < infileVec.size() ; ++ iin )
	{
		cout<<"ifile "<<iin<<"/"<<infileVec.size()<<endl ;
		string error1;
		bool ok1 = FillOneFileIntoOutput<T>(
			sumdata,
			cntdata,
			xsize,
			ysize,
			infileVec[iin],
			valid0,
			valid1,
			fillvalue,
			error1) ;
		if( ok1==false ){
			cout<<"bad FillOneFileIntoOutput run:"<<error1<<endl ;
			cout<<infileVec[iin]<<endl ;
			if( skipbad==true ) {
				cout<<"skip..."<<endl;
			}else{
				cout<<"exit."<<endl;
				return false ;
			}
		}
	}
	shared_ptr<wGdalRaster> outRas0( 
		wGdalRasterFactory::Create( 
			xsize ,
			ysize ,
			1,
			outType)
		) ;

	if( outRas0.get() == 0 )
	{
		cout<<"create outRas0 failed."<<endl ;
		return false ;
	}

	outRas0->copyProj( proj ) ;
	outRas0->copyTrans( trans ) ;

	if( type=="cnt" ) {
		for(int it = 0;it<asize;++it) 
		{
			outRas0->setValued(it,0, cntdata[it] ) ;
		}
	}else{
		for(int it = 0;it<asize;++it) 
		{
			outRas0->setValued(it,0, sumdata[it] ) ;
		}
	}
	cout<<"saving..."<<endl ;
	bool saveok1 = outRas0->save(outFilename,fillvalue,true) ;
	if( saveok1==false ){
		cout<<"failed to save "<<outFilename<<endl ;
		return false ;
	}
	cout<<"save good."<<endl ;
	return true ;
}


int main(int argc , char** argv )
{
	cout<<"A program to do count/sum composite for one band data, input files should be same size and proj."<<endl ;
	cout<<"单波段数据计数/求和合成，符合特定像素值的计数加1或者求和，反正其他情况保留0即可。"<<endl;
	cout<<"v1.0.0 2024/9/27"<<endl ;
	cout<<"usage:general_countOrSumCompose "<<endl;
	cout<<"    -m middle datatype: 1 u8,2 u16,3 i16,4 u32,5 i32,6 f32,7 f64"<<endl;
	cout<<"    -u output datatype "<<endl;
	cout<<"    -d directory ,first choice. if d is empty use tmpl and date."<<endl;
	cout<<"    -p filenamePrefix"<<endl;
	cout<<"    -t filenameTail"<<endl;
	cout<<"    -i valid min included."<<endl;
	cout<<"    -a valid max included."<<endl;
	cout<<"    -f fill value"<<endl ;
	cout<<"    -o output.tif"<<endl ;
	cout<<"    --type cnt or sum compose type."<<endl;
	cout<<"    --tmpl /some/{YYYY}/FileName_{YYYY}{MM}{DD}.TIF , this is second choice."<<endl ;
	cout<<"    --date 20231201,20240101,20240201 , this is second choice."<<endl ;
	cout<<"    --symd start date YYYYMMDD included, exclusive to --date."<<endl;
	cout<<"    --eymd end   date YYYYMMDD included, must used with --symd, exclusive to --date."<<endl;
	cout<<"    --skipbad  if skip bad file without exit?"<<endl;

	GDALAllRegister() ;
	int midtype = 6 ;
	int outtype = 6 ;
	double validMin = 9999 ;
	double validMax = -9999 ;
	double fillValue = 0 ;
	string outFilename;
	string inPrefix , inTail , inDir ;
	string templateStr, dateArrStr ;
	int symd = 0 ;
	int eymd = 0 ;
	bool skipbad = false;
	string type ;

	GetOpt_pp ops(argc,argv) ;
	ops>>Option('m', midtype)
		>>Option('u',outtype)
		>>Option('d',inDir)
		>>Option('p',inPrefix)
		>>Option('t',inTail)
		>>Option('i',validMin)
		>>Option('a',validMax)
		>>Option('f',fillValue)
		>>Option('o',outFilename)
		>>Option("tmpl",templateStr)
		>>Option("date",dateArrStr) 
		>>Option("symd",symd) 
		>>Option("eymd",eymd) 
		>>Option("type",type)
		>>OptionPresent("skipbad", skipbad);

	cout<<"------------"<<endl ;
	cout<<"midtype "<<GTYPE[midtype]<<endl ;
	cout<<"outtype "<<GTYPE[outtype]<<endl ;
	cout<<"inDir :"<<inDir<<endl ;
	cout<<"inPrefix :"<<inPrefix<<endl ;
	cout<<"inTail :"<<inTail<<endl ;
	cout<<"validMin :"<<validMin<<endl ;
	cout<<"validMax :"<<validMax<<endl ;
	cout<<"fillValue :"<<fillValue<<endl ;
	cout<<"outFilename :"<<outFilename<<endl ;
	cout<<"templateStr :"<<templateStr<<endl ;
	cout<<"dateArrStr :"<<dateArrStr<<endl ;
	cout<<"symd :"<<symd<<endl ;
	cout<<"eymd :"<<eymd<<endl ;
	cout<<"skipbad :"<<skipbad<<endl ;
	cout<<"compose type :"<<type<<endl;
	cout<<"------------"<<endl ;

	if( midtype==0 ) {
		cout<<"Error, bad midtype."<<endl;
		return 11 ;
	}
	if( outtype==0 ) {
		cout<<"Error, bad outtype."<<endl;
		return 11 ;
	}
	if( inDir !="" ) {
		cout<<"using inDir"<<endl;
		if( inPrefix=="") {
			cout<<"Error, bad inPrefix."<<endl;
			return 11 ;
		}
		if( inTail=="") {
			cout<<"Error, bad inTail."<<endl;
			return 11 ;
		}		
	}else {
		cout<<"using templateStr"<<endl;
		if( templateStr=="") {
			cout<<"Error, bad templateStr."<<endl;
			return 11 ;
		}
		if( dateArrStr!="" ) {
			cout<<"using dateArrStr."<<endl;
		}else if( symd!=0 && eymd!=0 ) {
			cout<<"using symd and eymd."<<endl;
		}else{
			cout<<"Error, either dateArrStr is empty nor symd/eymd."<<endl;
			return 11 ;
		}
	}
	if( type == "sum" || type == "cnt" ){
		//ok
	}else{
		cout<<"Error, unknow compose type '"<<type <<"'" <<endl;
		return 11 ;
	}
	
	if( outFilename=="") {
		cout<<"Error, bad outFilename."<<endl;
		return 11 ;
	}
	if( validMax < validMin ) {
		cout<<"Error, validMax is lower than validMin."<<endl;
		return 11 ;
	}


	vector<string> infileVec ;
	if( inDir!="" ) {
		wDirTools::getDirFilesRecursive2( inDir , inPrefix , inTail , infileVec ) ;
	}else{
		if( dateArrStr!="") {
			vector<string> dateArr = wStringUtils::splitString(dateArrStr,",") ;
			if( dateArr.size()==0 ) {
				cout<<"Error, dateArr is empty."<<endl;
				return 11 ;
			}
			for(int i = 0 ; i < dateArr.size(); ++ i ) {
				if( dateArr[i].length()==8 ) {
					string yyyy = dateArr[i].substr(0,4) ;
					string mm = dateArr[i].substr(4,2) ;
					string dd = dateArr[i].substr(6,2) ;
					string filename1 = wStringUtils::replaceString(templateStr,"{YYYY}",yyyy) ;
					filename1 = wStringUtils::replaceString(filename1,"{MM}",mm) ;
					filename1 = wStringUtils::replaceString(filename1,"{DD}",dd) ;
					cout<<"using filename "<<filename1<<endl;
					infileVec.push_back(filename1) ;
				}else{
					cout<<"Error, dateArr["<<i<<"] is not YYYYMMDD format."<<endl;
					return 11 ;
				}
			}
		}else{
			int year0 = symd/10000 ;
			int year1 = eymd/10000 ;
			vector<int> ymdArr;
			for(int iyear = year0 ; iyear <= year1; ++ iyear ) {
				for(int imon = 1 ; imon <=12;++imon ) {
					for(int iday=1; iday <=31; ++iday ) {
						int ymdx = iyear*10000+imon*100+iday;
						if( ymdx>=symd && ymdx<=eymd ) {
							ymdArr.push_back(ymdx) ;
						}
					}
				}
			}
			cout<<"ymdArr size "<<ymdArr.size()<<endl;
			if( ymdArr.size()== 0 ) {
				cout<<"Error, ymdArr is empty."<<endl;
				return 11 ;
			}
			for(int iday = 0 ; iday < ymdArr.size(); ++ iday ) {
				char buff[16] ;
				sprintf( buff , "%d" , ymdArr[iday] ) ;
				string str1(buff) ;
				string yyyy = str1.substr(0,4) ;
				string mm =   str1.substr(4,2) ;
				string dd =   str1.substr(6,2) ;
				string filename1 = wStringUtils::replaceString(templateStr,"{YYYY}",yyyy) ;
				filename1 = wStringUtils::replaceString(filename1,"{MM}",mm) ;
				filename1 = wStringUtils::replaceString(filename1,"{DD}",dd) ;
				if( wDirTools::isFileExist(filename1) ) {
					cout<<"using filename "<<filename1<<endl;
					infileVec.push_back(filename1) ;
				}
			}
		}
	}
	
	if( infileVec.size()==0 ){
		cout<<"Error, infileVec size equal 0. "<<endl ;
		return 13 ;
	}

	char projbuff[2048] ;
	double trans[6] ;
	GDALDataType outType ;

	// only one band ok.int theBandNum = 0 ;
	{
		bool atLeastOneOK = false ;
		int xsize = 0 ;
		int ysize = 0 ;
		int asize = 0 ;
		for(int ifile = 0 ; ifile < infileVec.size(); ++ ifile ) {
			//need one ok.
			wGdalRaster* tempPtr = wGdalRasterFactory::OpenFile( infileVec[ifile] ) ;
			if( tempPtr == 0 ){
				if( skipbad==true ) {
					cout<<"skip bad "<<infileVec[ifile]<<endl;
					continue ;
				}else{
					cout<<"bad inputfile at 0."<<endl ;
					return 14 ;
				}
			}
			atLeastOneOK = true ;
			const double* trans_0 = tempPtr->getTrans() ;
			for(int it=0;it<6;++it) trans[it] = trans_0[it] ;
			strcpy( projbuff , tempPtr->getProj() ) ;
			xsize = tempPtr->getXSize() ;
			ysize = tempPtr->getYSize() ;
			asize = xsize*ysize ;
			if( outtype==1 )
			{
				outType = GDT_Byte;
			}else if( outtype==2 )
			{
				outType = GDT_UInt16;
			}else if( outtype==3 )
			{
				outType = GDT_Int16;
			}else if( outtype==4 )
			{
				outType = GDT_UInt32;
			}else if( outtype==5 )
			{
				outType = GDT_Int32;
			}else if( outtype==6 )
			{
				outType = GDT_Float32;
			}else if( outtype==7 )
			{
				outType = GDT_Float64;
			}else{
				cout<<"bad outtype "<<outtype<<endl ;
				return 15;
			}
			break ;
		}
		if( atLeastOneOK==false ) {
			cout<<"all input file is bad."<<endl;
			return 16;
		}

		bool allok = false ;
		if( midtype==1 )
		{
			allok = doAllFiles<unsigned char>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type ) ;
		}else if( midtype==2 )
		{
			allok = doAllFiles<unsigned short>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type  ) ;
		}else if( midtype==3 )
		{
			allok = doAllFiles<short>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type  ) ;
		}else if( midtype==4 )
		{
			allok = doAllFiles<unsigned int>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type  ) ;
		}else if( midtype==5 )
		{
			allok = doAllFiles<int>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type  ) ;
		}else if( midtype==6 )
		{
			allok = doAllFiles<float>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type  ) ;
		}else if( midtype==7 )
		{
			allok = doAllFiles<double>( infileVec
				,xsize,ysize,validMin,validMax,fillValue,outType,trans,projbuff,outFilename,skipbad,type  ) ;
		}else{
			cout<<"bad midtype "<<midtype<<endl ;
			return 16;
		}

		if( allok==false )
		{
			cout<<"not all ok."<<endl ;
			return 17 ;
		}

	}	

	cout<<"done."<<endl ;

	return 0 ;
}
